package fr.lip6.meta.ple.plextraction;

import java.io.File;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.FileWriter;
import java.io.PrintStream;
import java.io.PrintWriter;
import java.util.ArrayList;
import java.util.Collection;
import java.util.HashMap;

import artefact.umlClassDiagram.CreateAttribute;
import artefact.umlClassDiagram.CreateClass;
import artefact.umlClassDiagram.CreateOperation;
import artefact.umlClassDiagram.CreatePackage;

import fr.lip6.meta.ple.constraintsextraction.constraint.Constraint;
//import fr.lip6.meta.ple.constraintsextraction.constraint.Expression;
//import fr.lip6.meta.ple.constraintsextraction.constraint.Not;
//import fr.lip6.meta.ple.constraintsextraction.constraint.Or;
import fr.lip6.meta.ple.featureIdentification.Feature;
import fr.lip6.meta.ple.featureIdentification.Product;
import fr.lip6.meta.tools.StatementPrimitive;
import fr.lip6.meta.tools.Trigger;

public class ProductLineConstruction {

	final static String path = "./SPL-Construction/features/";
	static Feature mondatory;
	static HashMap<String, ArrayList<Trigger>> classesBase;
	
    static void generateFeatureModel(Collection<Feature> features,ArrayList<Constraint> rules) {
    	
    	/*<?xml version="1.0" encoding="UTF-8" standalone="no"?>
    	<featureModel>
    		<struct>
    			<and abstract="true" mandatory="true" name="ZZZ">
    				<feature mandatory="true" name="Base"/>
    				<feature name="F1"/>
    				<feature name="F2"/>
    				<feature name="F3"/>
    				<feature name="F4"/>
    			</and>
    		</struct>
    		<constraints>
		<rule>
			<imp>
				<var>F0</var>
				<var>F1</var>
			</imp>
		</rule>
	</constraints>
    		<comments/>
    	</featureModel>*/

    	//create folder features withe files feautres 
    	String folderpath = "./features/";
		File folder = new File (folderpath);
		deleteRecursive(folder);
		folder.mkdirs();
		
		for(Feature f : features){
			try
			{
			FileWriter fw=new FileWriter(folderpath+f.getId());
			PrintWriter pw=new PrintWriter(fw);
			pw.println(f.toStringLines(1));
			pw.close();
		
			}
			catch(Exception e)
			{
				
				System.err.println(e.getMessage());
			}
			
		}
		//end create 
    
    		PrintStream out;
    		String rep = "./SPL-Construction/";
		File repit = new File (rep);
		deleteRecursive(repit);
		repit.mkdirs();
		
		File dotFile =new File(rep+"model.xml");
		FileOutputStream fout;
	
		
		try {
			fout = new FileOutputStream(dotFile);
			out = new PrintStream(fout);
			
			out.print("<?xml version=\"1.0\" encoding=\"UTF-8\" standalone=\"no\"?>"+"\n");
			out.println(	"<featureModel>");
			out.println("<struct>");
			out.println("<and abstract=\"true\" mandatory=\"true\" name=\"SPLConstruction\">");
			out.println("<feature mandatory=\"true\" name=\"Base\"/>");
			
			for (Feature f: features) {
				if (!f.getId().equals("F0")) 
					out.println("<feature name="+"\""+f.getId()+"\""+"/>");
			}
			
			out.println("</and>");
			out.println("</struct>");
			out.println("<constraints>");
			
			System.out.println("########CONTRAINTTTTTTT#######"+rules.size());
			for(Constraint rule:rules){
				out.println("<rule>");
				out.println("<imp>");
				
				
				
				out.println(rule.getLeft().toXML());
				
				out.println(rule.getRight().toXML());
			    out.println("</imp>");
			    out.println("</rule>");
			}
			
			
			out.println("</constraints>");
			out.println("<comments/>");
			
			out.println(	"</featureModel>");
		} catch (FileNotFoundException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
		
    }
	
	static void construct(Collection<Feature> features, Collection<Product> products) {
		
		
		System.out.println("#################### Software Product Line Construction #################");
		
		/*
		 * Traitement de la base 
		 */
		mondatory = findMondatory(features,products);
		
		if (mondatory!=null){
			
			handleMondatoryFeature(mondatory);
			//handleOptionalFeatures(features, products);
			
		}
		
		/*
		 * Traitement des optionals
		 */
		
		handleOptionalFeatures(features, products);
	}

	

	private static void handleOptionalFeatures(Collection<Feature> features,
			Collection<Product> products) {
		
		
		
		
		
		
		for(Feature f : features){
			
		   if (f!=mondatory){
			HashMap<String, ArrayList<Trigger>> refineClasses = new HashMap<String, ArrayList<Trigger>>();
			HashMap<String, ArrayList<Trigger>> newClassesFeatures = new HashMap<String, ArrayList<Trigger>>();
			if (f!=mondatory){
				
				for(Trigger t: f){
					StatementPrimitive st = (StatementPrimitive)t;
				     System.out.println("Trigger :"+t.getClass());
					
					if(st.getPrimitive() instanceof CreateClass){

						CreateClass c = (CreateClass)st.getPrimitive();
						if (!newClassesFeatures.containsKey(c.getName()))
							newClassesFeatures.put(c.getName(), new ArrayList<Trigger>());
						//generateNewClassFeature(c,f);
						
					}
					else {
						if(st.getPrimitive() instanceof CreateAttribute){
							
							CreateAttribute ca = (CreateAttribute)st.getPrimitive();
							System.out.println("CreateAttribute :"+ca.getName());
							if (classesBase.containsKey(ca.getOwner())){
								if(refineClasses.containsKey(ca.getOwner())){
									refineClasses.get(ca.getOwner()).add(t);
								}
								else {
									ArrayList<Trigger> tab	=new ArrayList<Trigger>();
									tab.add(t);
									refineClasses.put(ca.getOwner(), tab);
								
								}
								
							}
							// new class
							else {
								if(newClassesFeatures.containsKey(ca.getOwner())){
									newClassesFeatures.get(ca.getOwner()).add(t);
								}
								else {
									ArrayList<Trigger> tab	=new ArrayList<Trigger>();
									tab.add(t);
									newClassesFeatures.put(ca.getOwner(), tab);
								
								}
								
							}
							
						}
						else{
							
							if(st.getPrimitive() instanceof CreateOperation){
								CreateOperation co = (CreateOperation)st.getPrimitive();
								System.out.println("CreateOperation :"+co.getName());
								if (classesBase.containsKey(co.getOwener())){
	
								if(refineClasses.containsKey(co.getOwener())){
									refineClasses.get(co.getOwener()).add(t);
								}
								else {
									ArrayList<Trigger> tab	=new ArrayList<Trigger>();
									tab.add(t);
									refineClasses.put(co.getOwener(), tab);
								
						
								}
								
						   }
							//	
							
								// new class
								else {
									if(newClassesFeatures.containsKey(co.getOwener())){
										newClassesFeatures.get(co.getOwener()).add(t);
									}
									else {
										ArrayList<Trigger> tab	=new ArrayList<Trigger>();
										tab.add(t);
										newClassesFeatures.put(co.getOwener(), tab);
									
									}
									
								}
						}
							
							
						}
						
						
					}
					
				
				
				
				}//for
						
					
				}
			
			generateOptionalFeatures(path,f,refineClasses, newClassesFeatures);
		
		   }
		}
		
		
		
	}



	private static void generateOptionalFeatures(String path, Feature f,
			HashMap<String, ArrayList<Trigger>> refineClasses,
			HashMap<String, ArrayList<Trigger>> newClassesFeatures) {
		
		//  traitement new classes
		
		PrintStream out;
		String rep = path+f.getId()+"/";
		File repit = new File (rep);
		deleteRecursive(repit);
		repit.mkdirs();
		//print each class
		for(String cl : newClassesFeatures.keySet()){
			
			File dotFile =new File(rep+cl+".jak");
			FileOutputStream fout;
		
			try {
				fout = new FileOutputStream(dotFile);
				out = new PrintStream(fout);
				
				out.print("public class "+cl+"{"+"\n");
				
				
				for(Trigger t : newClassesFeatures.get(cl)){
					StatementPrimitive st = (StatementPrimitive)t;
					if (st.getPrimitive() instanceof CreateAttribute){
						
						System.out.println("PRINT Attribute");
						CreateAttribute ca = (CreateAttribute)st.getPrimitive();
					    out.println(ca.getType()+"  "+ca.getName()+";"); 
					    System.out.println(ca.getType()+"  "+ca.getName()+";"); 
					}
					
					if (st.getPrimitive() instanceof CreateOperation){
						System.out.println("PRINT Operation");
						CreateOperation co= (CreateOperation)st.getPrimitive();
						out.println("public void "+co.getName()+"(){\n");
						System.out.println("public void "+co.getName()+"(){\n");
						out.println("}");
						System.out.println("}");
					}
					
					
				}
				out.print("}\n");
			} catch (FileNotFoundException e) {
				// TODO Auto-generated catch block
				e.printStackTrace();
			}
		    
		
		}
		
		///////-------------
		
//  traitement refineclasses
		
	
		
		//repit = new File (path+f.getId());
		//deleteRecursive(repit);
		//repit.mkdirs();
		//print each class
		for(String cl : refineClasses.keySet()){
			
			File dotFile =new File(rep+cl+".jak");
			FileOutputStream fout;
		
			try {
				fout = new FileOutputStream(dotFile);
				out = new PrintStream(fout);
				
				out.print("public refines class "+cl+"{"+"\n");
				
				
				for(Trigger t : refineClasses.get(cl)){
					StatementPrimitive st = (StatementPrimitive)t;
					if (st.getPrimitive() instanceof CreateAttribute){
						
						System.out.println("PRINT Attribute");
						CreateAttribute ca = (CreateAttribute)st.getPrimitive();
					    out.println(ca.getType()+"  "+ca.getName()+";"); 
					    System.out.println(ca.getType()+"  "+ca.getName()+";"); 
					}
					
					if (st.getPrimitive() instanceof CreateOperation){
						System.out.println("PRINT Operation");
						CreateOperation co= (CreateOperation)st.getPrimitive();
						out.println("public void "+co.getName()+"(){\n");
						System.out.println("public void "+co.getName()+"(){\n");
						out.println("}");
						System.out.println("}");
					}
					
					
				}
				out.print("}\n");
			} catch (FileNotFoundException e) {
				// TODO Auto-generated catch block
				e.printStackTrace();
			}
		    
		
		}
		
		
		
		
	}



	private static void handleMondatoryFeature(Feature mondatory) {
		System.out.println("MONDATORY :"+mondatory.getId());
		
		ArrayList<CreatePackage> pack = findPackages(mondatory);
		
		System.out.println("Packages :"+pack.size());
		
		
		for(CreatePackage p:pack){
			
			HashMap<String, ArrayList<Trigger>> classes = findClasses(mondatory, p);
			classesBase = classes;
			generate(path+"/Base/", p,classes);
		}
	}



	public static void deleteRecursive(File f)  {
		if (f.exists()){
			if (f.isDirectory()){
			  File[] childs=f.listFiles();
			  int i=0;
			  for(i=0;i<childs.length;i++){
				  deleteRecursive(childs[i]);
			  }
			  
			}
			f.delete();
		 }
	}


	private static void generate(String path,CreatePackage p,
			HashMap<String, ArrayList<Trigger>> classes) {
		PrintStream out;
		
		File repit = new File (path);
		deleteRecursive(repit);
		repit.mkdirs();
		//print each class
		for(String cl : classes.keySet()){
			
			File dotFile =new File(path+cl+".jak");
			FileOutputStream fout;
		
			try {
				fout = new FileOutputStream(dotFile);
				out = new PrintStream(fout);
				//out.print("package "+p.getName()+";\n");
				System.out.print("package "+p.getName()+";\n");
				out.print("public class "+cl+"{"+"\n");
				
				
				for(Trigger t : classes.get(cl)){
					StatementPrimitive st = (StatementPrimitive)t;
					if (st.getPrimitive() instanceof CreateAttribute){
						
						System.out.println("PRINT Attribute");
						CreateAttribute ca = (CreateAttribute)st.getPrimitive();
					    out.println(ca.getType()+"  "+ca.getName()+";"); 
					    System.out.println(ca.getType()+"  "+ca.getName()+";"); 
					}
					
					if (st.getPrimitive() instanceof CreateOperation){
						System.out.println("PRINT Operation");
						CreateOperation co= (CreateOperation)st.getPrimitive();
						out.println("public void "+co.getName()+"(){\n");
						System.out.println("public void "+co.getName()+"(){\n");
						out.println("}");
						System.out.println("}");
					}
					
					
				}
				out.print("}\n");
			} catch (FileNotFoundException e) {
				// TODO Auto-generated catch block
				e.printStackTrace();
			}
		    
		
		}
	}

	


	private static HashMap<String, ArrayList<Trigger>> findClasses(
			ArrayList<Trigger> f, CreatePackage p) {
   		
		HashMap<String, ArrayList<Trigger>> result = new HashMap<String, ArrayList<Trigger>>();
		System.out.println("Feature:"+f);
		for(Trigger t:f){
			StatementPrimitive st = (StatementPrimitive)t;
		     System.out.println("Trigger :"+t.getClass());
			
			if(st.getPrimitive() instanceof CreateClass){
				
				CreateClass cl = (CreateClass)st.getPrimitive();
				System.out.println("CreateClass :"+cl.getName());
				if (cl.getOwener().equals(p.getName())){
					if (!result.containsKey(cl.getName()))
							result.put(cl.getName(), new ArrayList<Trigger>());
				}
			}
				else{
					if(st.getPrimitive() instanceof CreateAttribute){
						
						CreateAttribute ca = (CreateAttribute)st.getPrimitive();
						System.out.println("CreateAttribute :"+ca.getName());
						if(result.containsKey(ca.getOwner())){
							result.get(ca.getOwner()).add(t);
						}
						else {
							ArrayList<Trigger> tab	=new ArrayList<Trigger>();
							tab.add(t);
							result.put(ca.getOwner(), tab);
						
						}
						
					}
					else{
						
						if(st.getPrimitive() instanceof CreateOperation){
							CreateOperation co = (CreateOperation)st.getPrimitive();
							System.out.println("CreateOperation :"+co.getName());
							if(result.containsKey(co.getOwener())){
								result.get(co.getOwener()).add(t);
							}
							else {
								ArrayList<Trigger> tab	=new ArrayList<Trigger>();
								tab.add(t);
								result.put(co.getOwener(), tab);
							
							}
					}
					
					
				}
			}
		
		
		}
		return result;
}

	private static ArrayList<CreatePackage> findPackages(ArrayList<Trigger> set) {
		// TODO Auto-generated method stub
		ArrayList<CreatePackage> result = new ArrayList<CreatePackage>();
		for(Trigger t: set){
			//System.out.println("Trigger :"+t.getClass());
			StatementPrimitive st = (StatementPrimitive)t;
			
			if (st.getPrimitive() instanceof CreatePackage){
				result.add((CreatePackage)st.getPrimitive());
			}
		}
		return result;
	}



	
	
	private static Feature findMondatory(Collection<Feature> features,
			Collection<Product> products) {
		// TODO Auto-generated method stub
	//Feature result;
	ArrayList<Feature> allF = new ArrayList<Feature>(features);
	for(Feature f:features){
		
		for(Product p:products){
			if (!f.getProdIds().contains(p.getId())){
				
				allF.remove(f);
				break;
			}
		}
	}
	if (allF.size()==1) return allF.get(0);
	else return null;
	}

}
